﻿using System.ComponentModel.Composition;
using SS.Common.DBBuilder.DBDataStructure;
using System.Text;
using SS.Common.Logging;
using System.IO;
using System;
using SS.Net.AppGen.Models;
using System.Xml;
using System.Xml.Serialization;
using SS.Net.AppGen.Generators;

namespace SS.Net.AppGen.WebGenerators.WebControllerGenerator
{
    /// <summary>
    /// The generator is responsible for generating base controller class
    /// </summary>
    [Export(typeof(BaseControllerGenerator))]
    public sealed class BaseControllerGenerator : BaseGenerator, IWebComponentsGenerator
    {
        #region Fields

        private static class Consts
        {
            /// <summary>
            /// This value will be replaced at run time
            /// </summary>
            public const string ConstDefaultImage = "[DefaultImage]";

            /// <summary>
            /// This value will be replaced at run time
            /// </summary>
            public const string ConstUploadImageDirectory = "[UploadImageDirectory]";
        }

        /// <summary>
        /// The logger instance
        /// </summary>
        private readonly ILogger Logger = LoggerManager.GetLogger(typeof(BaseControllerGenerator).Name);

        /// <summary>
        /// The instance of _classComponents, this object will be updated when deserialization pattern file
        /// </summary>
        private ClassComponents _classComponents;

        #endregion

        #region Ctors

        /// <summary>
        /// Default constructor
        /// </summary>
        public BaseControllerGenerator()
        {
            this.XMLTemplate = "BaseControllerTemplate.xml";
            this.DirPath = Path.Combine(this.GetAppPath(), typeof(WebGenerator).Name, "Controllers");
            this.InitializeDir(this.DirPath);
            this.DeserializeTemplate();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Override this method to generate components
        /// </summary>
        /// <param name="db">Data source for generating</param>
        public void Generate(DataBase db)
        {
            this.GenerateContents(db);
        }

        /// <summary>
        /// Deserialize Base Controller pattern file
        /// </summary>
        private void DeserializeTemplate()
        {
            var patternFile = Path.Combine(this.GetAppPath(), "Templates", "WebTemplates", this.XMLTemplate);

            using (XmlTextReader xreader = new XmlTextReader(patternFile))
            {
                xreader.Namespaces = false;
                try
                {
                    var x = new XmlSerializer(typeof (ClassComponents));
                    this._classComponents = x.Deserialize(xreader) as ClassComponents;
                }
                catch (Exception ex)
                {
                    throw new Exception(
                        string.Format("Cannot deserialize BaseControllerTemplate, BaseControllerTemplate => {0}",
                                      ex.Message));
                }
            }
        }

        /// <summary>
        /// Method for generating contents of controller components and save generated contents to a file
        /// </summary>
        /// <param name="db">Data source for generating</param>
        private void GenerateContents(DataBase db)
        {
            this.Logger.Info("Generate BaseController --> Start");

            try
            {
                var contents = new StringBuilder(BaseConsts.MAX_BYTES);

                // Generating Using region
                contents.AppendLine(this._classComponents.UsingPart.Replace(BaseConsts.ConstGlobalNameSpace,
                                                                           this.GlobalNameSpace));
                contents.AppendLine(string.Empty);

                // Generating Class region
                contents.AppendLine(string.Format("namespace {0}.Controllers", this.GlobalNameSpace));
                contents.Append("{");
                contents.AppendLine(this._classComponents.ClassPart.Comment);
                contents.Append(BaseConsts.TAB1).AppendLine("public class BaseController : Controller");
                contents.Append(BaseConsts.TAB1).AppendLine("{");

                // Generating Regions Fields
                contents.Append(this.GenerateRegionFields(db));

                // Generating Regions Ctors
                contents.Append(this.GenerateRegionCtors(db));

                // Generating Regions Utils
                contents.Append(this.GenerateRegionUtils());

                contents.Append(BaseConsts.TAB1).AppendLine("}");
                contents.AppendLine("}");

                // Save controller contents to a file
                this.SaveFile(contents.ToString().TrimStart(), "BaseController.cs");
            }
            catch (Exception ex)
            {
                this.Logger.ErrorFormat("GenerateContents Error: {0}", ex.ToString());
                throw new Exception(ex.ToString());
            }

            this.Logger.Info("Generate BaseController <-- End");
        }

        /// <summary>
        /// The method for generating region fields
        /// </summary>
        /// <param name="db">Data source for generating</param>
        /// <returns></returns>
        private string GenerateRegionFields(DataBase db)
        {
            var contents = new StringBuilder(BaseConsts.MAX_BYTES);

            // Generating Regions Fields
            contents.Append(BaseConsts.TAB2).AppendLine("#region Repositories");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            foreach (Table tbl in db.Tables)
            {
                // Skips a Table if it is existent in Skip array
                if (!this.IsItemExistent(BaseConsts.SkipTables, tbl.Name))
                {
                    contents.Append(BaseConsts.TAB2).AppendLine("/// <summary>");
                    contents.Append(BaseConsts.TAB2).AppendLine(
                        string.Format("/// The instance of {0}Repository for accessing table {1} in database", tbl.Name,
                                      tbl.Name));
                    contents.Append(BaseConsts.TAB2).AppendLine("/// </summary>");
                    contents.Append(BaseConsts.TAB2).AppendLine(
                        string.Format("public {0}Repository Rep{1} {{ get; set; }}", tbl.Name, tbl.Name));
                    contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);
                }
            }

            contents.Append(BaseConsts.TAB2).AppendLine("#endregion");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            return contents.ToString();
        }

        /// <summary>
        /// The method for generating region Ctors
        /// </summary>
        /// <param name="db">Data source for generating</param>
        /// <returns></returns>
        private string GenerateRegionCtors(DataBase db)
        {
            var contents = new StringBuilder(BaseConsts.MAX_BYTES);

            // Generating Regions Fields
            contents.Append(BaseConsts.TAB2).AppendLine("#region Ctors");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            contents.Append(BaseConsts.TAB2).AppendLine("/// <summary>");
            contents.Append(BaseConsts.TAB2).AppendLine("/// Base constructor");
            contents.Append(BaseConsts.TAB2).AppendLine("/// </summary>");
            contents.Append(BaseConsts.TAB2).AppendLine("public BaseController()");
            contents.Append(BaseConsts.TAB2).AppendLine("{");

            foreach (Table tbl in db.Tables)
            {
                // Skips a Table if it is existent in Skip array
                if (!this.IsItemExistent(BaseConsts.SkipTables, tbl.Name))
                {
                    contents.Append(BaseConsts.TAB3).AppendLine(
                        string.Format("this.Rep{0} = new {1}Repository(new {2}());", tbl.Name, tbl.Name, GlobalNameSpace + "Context"));
                }
            }

            contents.Append(BaseConsts.TAB2).AppendLine("}");

            contents.Append(BaseConsts.TAB2).AppendLine("#endregion");
            contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

            return contents.ToString();
        }

        /// <summary>
        /// The method for generating region Utils
        /// </summary>
        /// <returns></returns>
        private string GenerateRegionUtils()
        {
            var contents = new StringBuilder(BaseConsts.MAX_BYTES);

            // Generating Regions
            foreach (Region region in this._classComponents.ClassPart.Regions)
            {
                contents.Append(BaseConsts.TAB2).AppendLine(string.Format("#region {0}", region.RegionName));
                contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);

                // Generating Item
                foreach (Item item in region.Items)
                {
                    contents.AppendLine(item.Content.Replace(Consts.ConstDefaultImage,
                                                             WebConfiguration.Instance.Settings.DefaultImage)
                                            .Replace(Consts.ConstUploadImageDirectory,
                                                     WebConfiguration.Instance.Settings.UploadImageDirectory));
                    contents.AppendLine(string.Empty);
                }

                contents.Append(BaseConsts.TAB2).AppendLine("#endregion");
                contents.Append(BaseConsts.TAB2).AppendLine(string.Empty);
            }

            return contents.ToString();
        }

        #endregion
    }
}
